package com.jingdianjichi.club.gateway.auth;

import cn.dev33.satoken.stp.StpInterface;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.google.gson.Gson;
import com.jingdianjichi.club.gateway.redis.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

/**
 * Sa-Token权限验证接口实现类
 * 实现StpInterface接口，为Sa-Token框架提供用户权限和角色的获取逻辑
 * 主要功能：
 * 1. 从Redis缓存中获取用户的角色列表
 * 2. 根据角色获取对应的权限列表
 * 3. 支持多种权限数据格式的解析（JSON数组、逗号分隔字符串、单个权限）
 *
 * @author: GUTOU
 * @date: 2025/8/27
 */
@Component
@Slf4j
public class StpInterfaceImpl implements StpInterface {

    /**
     * Redis工具类，用于操作Redis缓存
     */
    @Resource
    private RedisUtil redisUtil;

    /**
     * 权限缓存key的前缀
     * 完整格式：auth.permission:{roleKey}
     * 用于存储角色对应的权限列表
     */
    private String authPermissionPrefix = "auth.permission";

    /**
     * 角色缓存key的前缀
     * 完整格式：auth.role:{username}
     * 用于存储用户对应的角色列表
     */
    private String authRolePrefix = "auth.role";

    /**
     * 获取用户权限列表
     * Sa-Token框架在进行权限校验时会调用此方法
     * 
     * 实现逻辑：
     * 1. 先根据用户ID获取用户的角色列表
     * 2. 遍历每个角色，获取角色对应的权限
     * 3. 将所有权限合并返回
     * 
     * @param loginId 登录用户ID（通常是用户的openid或username）
     * @param loginType 登录类型（Sa-Token支持多账户体系，这里暂未使用）
     * @return 用户拥有的所有权限列表
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        log.info("获取用户权限列表，loginId: {}, loginType: {}", loginId, loginType);
        
        // 第一步：根据用户id查询出对应角色
        // 从Redis中获取格式为 auth.role:{loginId} 的数据
        List<String> roles = this.getAuth(loginId.toString(), authRolePrefix);
        
        // 第二步：根据查询出来的角色查询对应的权限
        // 使用LinkedList存储权限列表，支持重复权限的去重
        LinkedList<String> permissionList = new LinkedList<>();
        
        // 遍历用户的每个角色
        roles.forEach(role -> {
            // 构建权限缓存key：auth.permission:{role}
            String permissionKey = redisUtil.buildKey(authPermissionPrefix, role);
            
            // 从Redis中查询该角色对应的权限数据
            String permission = redisUtil.get(permissionKey);
            
            // 检查权限字符串是否为空
            if (StringUtils.isNotBlank(permission)) {
                try {
                    // 优先尝试解析JSON数组格式的权限数据
                    // 例如：["subject:add", "subject:delete", "subject:update"]
                    List<String> permissions = new Gson().fromJson(permission, List.class);
                    if (permissions != null) {
                        permissionList.addAll(permissions);
                    }
                } catch (Exception e) {
                    // 如果JSON解析失败，说明可能是其他格式的权限数据
                    // 兼容处理逗号分隔的权限字符串
                    if (permission.contains(",")) {
                        // 处理逗号分隔格式："subject:add,subject:delete,subject:update"
                        String[] permissions = permission.split(",");
                        for (String perm : permissions) {
                            if (StringUtils.isNotBlank(perm.trim())) {
                                permissionList.add(perm.trim());
                            }
                        }
                    } else {
                        // 处理单个权限字符串："subject:add"
                        permissionList.add(permission.trim());
                    }
                }
            }
        });
        
        return permissionList;
    }

    /**
     * 获取用户角色列表
     * Sa-Token框架在进行角色校验时会调用此方法
     * 
     * @param loginId 登录用户ID（通常是用户的openid或username）
     * @param loginType 登录类型（Sa-Token支持多账户体系，这里暂未使用）
     * @return 用户拥有的角色列表
     */
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        log.info("获取用户角色列表，loginId: {}, loginType: {}", loginId, loginType);
        // 直接调用通用的权限获取方法，传入角色前缀
        return getAuth(loginId.toString(), authRolePrefix);
    }

    /**
     * 通用的权限/角色获取方法
     * 从Redis中获取指定前缀和用户ID对应的权限或角色数据
     * 
     * 注意事项：
     * 1. 该方法假设Redis中存储的数据格式为JSON数组
     * 2. 如果数据不存在或为空，返回空列表而不是null
     * 3. 数据格式示例：["admin", "user"] 或 ["subject:add", "subject:delete"]
     * 
     * @param loginId 登录用户ID
     * @param prefix 缓存key前缀（auth.role 或 auth.permission）
     * @return 权限或角色列表，如果不存在则返回空列表
     */
    private List<String> getAuth(String loginId, String prefix) {
        // 构建完整的Redis key：prefix:{loginId}
        String authKey = redisUtil.buildKey(prefix, loginId.toString());
        
        // 从Redis中获取对应的权限/角色数据
        String authValue = redisUtil.get(authKey);
        
        // 如果数据为空，返回空列表（避免返回null导致的空指针异常）
        if (StringUtils.isBlank(authValue)) {
            return Collections.emptyList();
        }
        
        // 使用Gson解析JSON数组格式的数据
        // 注意：这里假设Redis中存储的是JSON数组格式
        List<String> authList = new Gson().fromJson(authValue, List.class);
        return authList;
    }

}